package com.zang.blogz.handler;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zang.blogz.dto.ChatRecordDTO;
import com.zang.blogz.dto.RecallMessageDTO;
import com.zang.blogz.dto.WebsocketMessageDTO;
import com.zang.blogz.enmus.ChatTypeEnum;
import com.zang.blogz.enmus.FilePathEnum;
import com.zang.blogz.entity.ChatRecord;
import com.zang.blogz.entity.UserInfo;
import com.zang.blogz.model.input.ro.VoiceRO;
import com.zang.blogz.service.ChatRecordService;
import com.zang.blogz.service.UserInfoService;
import com.zang.blogz.strategy.context.UploadStrategyContext;
import com.zang.blogz.utils.BeanCopyUtils;
import com.zang.blogz.utils.IpUtil;
import io.github.vampireachao.stream.core.optional.Opp;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @Author: ZVerify
 * @Description: websocket服务
 * @DateTime: 2022/9/6 14:03
 **/
@Data
@Service
@ServerEndpoint(value = "/ws-connect")
public class ZVerifyWebSocketHandler extends TextWebSocketHandler {


    private static ChatRecordService chatRecordService;

    @Autowired
    public void setChatRecordDao(ChatRecordService chatRecordService) {
        ZVerifyWebSocketHandler.chatRecordService = chatRecordService;
    }

    private static UserInfoService userInfoService;

    @Autowired
    public void setUserInfoService(UserInfoService userInfoService) {
        ZVerifyWebSocketHandler.userInfoService = userInfoService;
    }

    private static UploadStrategyContext uploadStrategyContext;

    @Autowired
    public void setUploadStrategyContext(UploadStrategyContext uploadStrategyContext) {
        ZVerifyWebSocketHandler.uploadStrategyContext = uploadStrategyContext;
    }
    /**
     * 固定前缀
     */
    public static String HEADER_NAME = "X-Real-IP";

    /**
     * 存放Session集合，方便推送消息
     */
    private static ConcurrentHashMap<String, WebSocketSession> webSocketSessionMaps = new ConcurrentHashMap<>();



    // 监听：连接开启
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        // put到集合，方便后续操作
        String userId = session.getAttributes().get("userId").toString();
        webSocketSessionMaps.put(HEADER_NAME + userId, session);
        // 更新在线人数
        updateOnlineCount();

        // 加载历史聊天记录
        ChatRecordDTO chatRecordDTO = listChartRecords(session);

        // 发送消息
        WebsocketMessageDTO messageDTO = WebsocketMessageDTO.builder()
                .type(ChatTypeEnum.HISTORY_RECORD.getType())
                .data(chatRecordDTO)
                .build();
        synchronized (session) {
            session.sendMessage(new TextMessage(JSON.toJSONString(messageDTO)));
        }
        // 给个提示
        String tips = "Web-Socket 连接成功，sid=" + session.getId() + "，userId=" + userId;
        System.out.println(tips);

    }

    /**
     * 加载历史聊天记录
     *
     * @param session session
     * @return 加载历史聊天记录
     */
    private ChatRecordDTO listChartRecords(WebSocketSession session) {

        String ipAddress = session.getAcceptedProtocol();

        LambdaQueryWrapper<ChatRecord> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.ge(ChatRecord::getCreateTime, DateUtil.offsetHour(new Date(), -12));

        return ChatRecordDTO.builder()
                .chatRecordList(chatRecordService.list(queryWrapper))
                .ipAddress(ipAddress)
                .ipSource(IpUtil.getIpSource(ipAddress))
                .build();
    }

    private void updateOnlineCount() throws IOException {

        // 获取当前在线人数
        WebsocketMessageDTO messageDTO = WebsocketMessageDTO.builder()
                .type(ChatTypeEnum.ONLINE_COUNT.getType())
                .data(webSocketSessionMaps.size())
                .build();
        // 广播消息
        broadcastMessage(messageDTO);
    }

    // 监听：连接关闭
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status){
        // 从集合移除
        String userId = session.getAttributes().get("userId").toString();
        webSocketSessionMaps.remove(HEADER_NAME + userId);

    }

    // 收到消息
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {

        String ipAddress = null;
        WebsocketMessageDTO messageDTO = JSONUtil.toBean(message.getPayload(), WebsocketMessageDTO.class, false);
        switch (Objects.requireNonNull(ChatTypeEnum.getChatType(messageDTO.getType()))) {
            case SEND_MESSAGE:

                String data = String.valueOf(messageDTO.getData()) ;
                InetAddress address = Objects.requireNonNull(session.getLocalAddress()).getAddress();
                if (Opp.of(address).isPresent()){

                    ipAddress = address.getHostAddress();
                }


                String userId = session.getAttributes().get("userId").toString();
                UserInfo byId = userInfoService.getById(Integer.valueOf(userId));

                // 发送消息
                ChatRecord chatRecord = new ChatRecord();

                chatRecord.setContent(data);
                chatRecord.setType(messageDTO.getType());
                chatRecord.setAvatar(byId.getAvatar());
                chatRecord.setNickname(byId.getNickname());
                chatRecord.setUserId(byId.getId());
                chatRecord.setIpAddress(ipAddress);
                String ipSource = IpUtil.getIpSource(ipAddress);
                chatRecord.setIpSource(ipSource);
                chatRecordService.save(chatRecord);

                messageDTO.setData(chatRecord);
                // 广播消息
                broadcastMessage(messageDTO);
                break;
            case RECALL_MESSAGE:
                // 撤回消息
                RecallMessageDTO recallMessage = JSON.parseObject(JSON.toJSONString(messageDTO.getData()), RecallMessageDTO.class);
                // 删除记录
                chatRecordService.removeById(recallMessage.getId());
                // 广播消息
                broadcastMessage(messageDTO);
                break;
            case HEART_BEAT:
                // 心跳消息
                messageDTO.setData("pong");
                session.sendMessage(new TextMessage((JSON.toJSONString(messageDTO))));

            default:
                break;
        }
    }

    // -----------

    // 向指定客户端推送消息
    public static void sendMessage(WebSocketSession session, String message) {
        try {
            System.out.println("向sid为：" + session.getId() + "，发送：" + message);
            session.sendMessage(new TextMessage(message));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 向指定用户推送消息
    public static void sendMessage(long userId, String message) {
        WebSocketSession session = webSocketSessionMaps.get(HEADER_NAME + userId);
        if(session != null) {
            sendMessage(session, message);
        }
    }

    /**
     * 广播消息
     *
     * @param messageDTO 消息dto
     * @throws IOException io异常
     */
    private void broadcastMessage(WebsocketMessageDTO messageDTO) throws IOException {

        Collection<WebSocketSession> sessions = webSocketSessionMaps.values();

        for (WebSocketSession webSocketService : sessions) {
            synchronized (webSocketService){
                TextMessage textMessage = new TextMessage(JSON.toJSONString(messageDTO));
                webSocketService.sendMessage(textMessage);
            }

        }
    }

    /**
     * 发送语音
     *
     * @param voiceRO 语音路径
     */
    public void sendVoice(VoiceRO voiceRO) {
        // 上传语音文件
        String content = uploadStrategyContext.executeUploadStrategy(voiceRO.getFile(), FilePathEnum.VOICE.getPath());
        voiceRO.setContent(content);
        // 保存记录
        ChatRecord chatRecord = BeanCopyUtils.copyObject(voiceRO, ChatRecord.class);
        chatRecordService.save(chatRecord);
        // 发送消息
        WebsocketMessageDTO messageDTO = WebsocketMessageDTO.builder()
                .type(ChatTypeEnum.VOICE_MESSAGE.getType())
                .data(chatRecord)
                .build();
        // 广播消息
        try {
            broadcastMessage(messageDTO);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



}
